package wslib

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"strings"
	"sync"

	"github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{}

type WsServer struct {
	host     string         // 主机地址
	port     int            // 主机端口
	path     string         // 服务路径, websocket服务地址：ws://ip:port/path
	srv      *http.Server   // http服务，连接建立后升级成websocket服务
	handler  SessionHandler // 会话回调
	wait     sync.WaitGroup // 等待退出
	sessions sync.Map       // 保存全部会话
}

// 处理连接请求，升级为websocket连接，建立会话
func (ws *WsServer) handleConnection(w http.ResponseWriter, r *http.Request) {
	c, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Print("upgrade:", err)
		return
	}
	WsSessionStart(c, ws)
}

// 开始websocket服务，需要传入会话回调
func StartWsServer(host string, port int, path string, handler SessionHandler) *WsServer {
	path = strings.TrimLeft(path, "/")
	path = "/" + path
	server := &WsServer{
		host:    host,
		port:    port,
		path:    path,
		handler: handler,
	}
	addr := host + ":" + fmt.Sprint(port)
	fmt.Println(addr)
	server.srv = &http.Server{Addr: addr}
	http.HandleFunc(path, server.handleConnection)
	wait := sync.WaitGroup{}
	wait.Add(1)
	go func() {
		defer func() {
			// 等待监听服务退出
			wait.Done()
			fmt.Println("#exit listenandserve")
		}()
		// 开始http监听服务
		if err := server.srv.ListenAndServe(); err != http.ErrServerClosed {
			log.Fatalf("ListenAndServe err:%s", err)
		}
	}()
	return server
}

// 主动停止会话
func (ws *WsServer) Stop() {
	// 关闭http监听服务
	if err := ws.srv.Shutdown(context.TODO()); err != nil {
		panic(err)
	}
	// 退出会话
	ws.sessions.Range(func(key, value interface{}) bool {
		session, ok := value.(*WsSession)
		if ok {
			session.Stop()
		}
		return true
	})
	// 等待服务退出
	ws.wait.Wait()
}

// 会话新建回调，保存会话
func (ws *WsServer) WSSessionCreated(session *WsSession) bool {
	ws.sessions.Store(session.conn.RemoteAddr().String(), session)
	ws.handler.WSSessionCreated(session)
	return true
}

// 会话关闭回调，删除会话
func (ws *WsServer) WSSessionClosed(session *WsSession) {
	ws.sessions.Delete(session.conn.RemoteAddr().String())
	ws.handler.WSSessionClosed(session)
}

// 会话数据回调
func (ws *WsServer) WSSessionReceived(session *WsSession, tp int, data []byte) bool {
	ws.handler.WSSessionReceived(session, tp, data)
	return true
}
